package org.dfl.core.data.types;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * Type class represents a data type in a dataflow. 
 * Each type defines a type of connected type containers (parents set). To connect 2 containers their
 * types have to be mergable (result of canMerge has to be true). After connecting 2 type containers
 * their types are merged and the result type becomes a type for both connected type containers. 
 *
 * @author Piotr Wlodarczyk
 */
public abstract class Type {
	/** type containers that are connected together and have this type **/
	private Set<TypeContainer> parents = new HashSet<TypeContainer>();
	
	/**
	 * create a type not assigned to any type container
	 */
	public Type() {}

	/**
	 * assign to new type container
	 * @param p - type container
	 */
	public void addParent(TypeContainer p){
		if(!parents.contains(p)){
			parents.add(p);
		}
	}
	
	/**
	 * remove assignment to a type container
	 * @param p - type container
	 */
	public void removeParent(TypeContainer p){
		if(parents.contains(p)){
			parents.remove(p);
		}
	}
	
	public Collection<TypeContainer> getParents() {
		return parents;
	}

	protected void mergeParentsWith(Type typeToRemove){
		Collection<TypeContainer> newParetns = new ArrayList<TypeContainer>(typeToRemove.getParents());
		for(TypeContainer p : newParetns){
			p.setType(this);
			typeToRemove.removeParent(p);
		}
	}
	
	protected void addParents(Collection<TypeContainer> targetParents){
		for(TypeContainer p : targetParents){
			addParent(p);
		}
	}
	
	/**
	 * check if this type can be merged with other type
	 * @param t - other type
	 * @return true/false
	 */
	public abstract boolean canMerge(Type t);

	/**
	 * merge this type with other one
	 * @param t - type to merge with
	 * @return true if merged, false if failed
	 */
	public boolean merge(Type t){
		return (canMerge(t) && complain(t));
	}
	
	protected abstract boolean complain(Type t);

	/**
	 * create a copy of this type. The copy is not assigned to any type container.
	 * @return type copy
	 */
	public abstract Type getCopy();
	
	/**
	 * save type into a string
	 * @return string representation of this type
	 */
	public abstract String save();
	
	@Override
	public String toString() {
		return save();
	}
}
